<template>
  <div class="full">
    <div class="threeCanvas full" ref="threeCanvas"></div>
  </div>
</template>

<script>
  import { 
    Scene, PerspectiveCamera, WebGLRenderer, AxesHelper, PlaneGeometry,
    MeshBasicMaterial, MeshStandardMaterial, MeshLambertMaterial,
    Mesh, BoxGeometry, SphereGeometry, SpotLight, GridHelper, Color,
    AmbientLight, PointLight, CubeTextureLoader, Raycaster, Vector2, Fog,
  } from 'three'
  import { GUI } from 'three/examples/jsm/libs/lil-gui.module.min.js';
  import { GLTFLoader } from '../plugin/three/examples/jsm/loaders/GLTFLoader';
  import { OrbitControls } from '../plugin/three/examples/jsm/controls/OrbitControls.js';
  
  import { EffectComposer } from '../plugin/three/examples/jsm/postprocessing/EffectComposer';
  import { RenderPass } from '../plugin/three/examples/jsm/postprocessing/RenderPass';
  import { OutlinePass } from '../plugin/three/examples/jsm/postprocessing/OutlinePass';


  export default {
    name: 'Home',
    
    data() {
      return {
        scene: null,
        fog: null,
        camera: null,
        renderer: null,
        INTERSECTED: null,
        controls: null,


        cattleList: [
          { id: '1001', name: '牛1'},
          { id: '1002', name: '牛2'},
          { id: '1003', name: '牛3'},
          { id: '1004', name: '牛4'},
        ]
      }
    },
    created() {

    },
    mounted () {
      this.initThreeCanvas()
    },
    destroyed() {

    },
    methods: {
      initThreeCanvas() {
        this.createScene()

        this.createCamera()

        this.createAmbientLight()

        this.createGridHelper()

        this.createPlaneGeometry()

        this.createRenderer()

        this.loadGLTF().then(gltf => {
          // 添加位置
          gltf.scene.position.set(0, 2, 0)
          gltf.scene.rotation.y = 0.5 * Math.PI

          this.scene.add(gltf.scene);

          this.renderer.render(this.scene, this.camera)
        })

        loadFengShan() {}

        // 遍历牲畜 列表 显示 牲畜 模型
        for (let i=0; i<this.cattleList.length; i++) {
          this.loadCattle().then(gltf => {
            // 添加位置
            gltf.scene.position.set(i+3, 1.3, 3)
            gltf.scene.scale.set(1.3, 1.3, 1.3)
            gltf.scene.rotation.y = 1 * Math.PI

            // 引入的模型 如何跟 后台数据 绑定
            // gltf.scene['x'] = { name: this.cattleList[i].id }

            this.scene.add(gltf.scene);

            this.renderer.render(this.scene, this.camera)
          })
        }

        // 监听 鼠标点击后 选中模型
        this.listenClick()

        // 显示
        this.$refs.threeCanvas.appendChild(this.renderer.domElement)

        //创建控件对象
        this.createControls()

        // 开启 动画
        // this.animate()
      },

      // 创建 场景
      createScene() {
        this.scene = new Scene();

        // 添加 背景颜色
        // this.scene.background = new Color(0x666666)

        // 添加 背景图片
        // let textureCube = new CubeTextureLoader().load(['/sheep.png', '/sheep.png', '/sheep.png', '/sheep.png', '/sheep.png', '/sheep.png'])

        // const material = new MeshBasicMaterial( { color: 0xffffff, envMap: textureCube } );

        // this.scene.background = textureCube
        // this.scene.environment = textureCube
      },

      // 添加 雾
      createFog() {
        let fog = new Fog(0xcccccc, 100, 120)

         // 添加 雾
         this.scene.fog = fog
      },

      // 创建 摄像机
      createCamera() {
        // (观看夹角、宽高比、最近距离、最远距离)
        this.camera = new PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000 );

        // 设置 摄像机 位置
        this.camera.position.set(0, 20, 40)

        // 设置 相机朝向
        this.camera.lookAt(this.scene.position)
      },

       // 添加 聚光灯
      createSpotLight() {
        const spotLight = new SpotLight(0xffffff)
        // 投射阴影
        spotLight.castShadow = true
        spotLight.position.set(-40, 60, -10)
        this.scene.add(spotLight)
      },

       // 添加 点光源
      createPointLight() {
        const pointLight = new PointLight(0xffffff)
        // 投射阴影
        // pointLight.castShadow = true
        pointLight.position.set(400, 200, 300)

        this.scene.add(pointLight)
      },

      // 添加 环境光
      createAmbientLight() {
        const ambientLight = new AmbientLight(0xffffff, 4);
        
        this.scene.add(ambientLight);
      },

      // 添加 坐标系 (大小)
      createAxes() {
        const axes = new AxesHelper(30)
        this.scene.add(axes)
      },

      // 渲染 
      createRenderer() {
        this.renderer = new WebGLRenderer();
        // this.renderer.setClearColor('#ffffff')
        this.renderer.setSize(window.innerWidth, window.innerHeight);
        this.renderer.shadowMap.enabled = true
        // 设置背景透明
        this.renderer.setClearAlpha(0.0);
      },

      // 加载 模型
      async loadGLTF() {
        let loader = new GLTFLoader();
        let gltf = await loader.loadAsync('/models/peng.gltf');
      
        return gltf;
      },

      // 加载 风扇
      async loadFengShan() {
        let loader = new GLTFLoader();
        let gltf = await loader.loadAsync('/models/feng-shan.gltf');
      
        return gltf;
      },

      async loadCattle() {
        let loader = new GLTFLoader();
        let gltf = await loader.loadAsync('/models/cattle.gltf');
      
        return gltf;
      },

      // 添加 地面 网格
      createGridHelper() {
        let gridHelper = new GridHelper(90, 20)
        this.scene.add(gridHelper)
      },

       // 添加 平面 几何体 PlaneGeometry
      createPlaneGeometry() {
        const planeGeometry = new PlaneGeometry(80, 20)
        const meshBasicMaterial = new MeshLambertMaterial({
          color: '#ccc'
        })
        const plane = new Mesh(planeGeometry, meshBasicMaterial)
        plane.receiveShadow = true
        plane.caseShadow = true
        // 逆时针 旋转 90°
        plane.rotation.x = -0.5 * Math.PI

        // 移动
        plane.position.set(4, 0, 0)
        this.scene.add(plane)
      },

      // 添加 立方体 BoxGeometry
      createCubeGeometry() {
        const cubeGeometry = new BoxGeometry(2, 2, 2)
        const cubeMeterial = new MeshLambertMaterial({
          color: '#f00',

          // fasle: 立方体 、true: 线框体
          wireframe: false
        })
        const cube = new Mesh(cubeGeometry, cubeMeterial)
        cube.castShadow = true
        cube.position.set(4, 10, 10)
        this.scene.add(cube)
      },

      // 添加 球体 BoxGeometry
      createSphereGeometry() {
        const sphereGeometry = new SphereGeometry(3)
        const sphereMeterial = new MeshLambertMaterial({
          color: '#2366fc',

          // fasle: 立方体 、true: 线框体
          wireframe: true
        })

        const sphere = new Mesh(sphereGeometry, sphereMeterial)
        sphere.castShadow = true
        sphere.position.set(10, 3, 5)
        this.scene.add(sphere) 
      },

      // 创建 GUI 对象
      createGui() {
        const gui = new GUI();

        const obj = { x: 40 }

        gui.add(document, 'title')
      },

      // 监听鼠标 点击 （射线拾取功能）
      listenClick() {
        this.renderer.domElement.addEventListener('click', event => {
          // .offsetY、.offsetX以canvas画布左上角为坐标原点,单位px
          const px = event.offsetX;
          const py = event.offsetY;

          let width = this.$refs.threeCanvas.offsetWidth
          let height = this.$refs.threeCanvas.offsetHeight

          //屏幕坐标px、py转WebGL标准设备坐标x、y
          //width、height表示canvas画布宽高度
          const x = (px / window.innerWidth) * 2 - 1;
          const y = -(py / window.innerHeight) * 2 + 1;

          //创建一个射线投射器`Raycaster`
          const raycaster = new Raycaster();

          //.setFromCamera()计算射线投射器`Raycaster`的射线属性.ray
          // 形象点说就是在点击位置创建一条射线，射线穿过的模型代表选中
          raycaster.setFromCamera(new Vector2(x, y), this.camera);

          //.intersectObjects([mesh1, mesh2, mesh3])对参数中的网格模型对象进行射线交叉计算

          // console.log('this.scene : ', this.scene);
          const intersects = raycaster.intersectObjects(this.scene.children);

          console.log("射线器返回的对象", intersects);

          // intersects.length大于0说明，说明选中了模型

          // 如果有相交的物体
          if (intersects.length > 0) {
            // 添加 描边
            // let composer = new EffectComposer(this.renderer);
            // let outlinePass = new OutlinePass(new Vector2(x, y), this.scene, this.camera)
            // outlinePass.selectedObjects = [intersects[0].object.ancestors];
            // composer.addPass(outlinePass)

            if (this.INTERSECTED != intersects[0].object) {
              // 这里选中的物体是上一个选中物体。
              if (this.INTERSECTED) {

                // console.log('上一个选中的模型 : ', this.INTERSECTED);

                // 把上一个选中的物体设置为当前色。
                if (this.INTERSECTED.material.emissive) {
                  this.INTERSECTED.material.emissive.setHex(this.INTERSECTED.currentHex);
                }
              }
              // 设置当前选中的物体
              this.INTERSECTED = intersects[0].object;

              // console.log('当前选中的模型 : ', this.INTERSECTED);

              if (this.INTERSECTED.material.emissive) {
                // 保留当前选中物体，**原本的颜色**
                this.INTERSECTED.currentHex = this.INTERSECTED.material.emissive.getHex();
                // 设置当前选中的物体颜色为红色
                this.INTERSECTED.material.emissive.setHex(0xff0000);
              }
            }
              // 如果没有相交的物体，把选中的物体设置为原来的颜色
          } else {
            if (this.INTERSECTED) {
              this.INTERSECTED.material.emissive.setHex(this.INTERSECTED.currentHex);
            }
            // 清空选中物体
            this.INTERSECTED = null;
          }

          this.renderer.render(this.scene, this.camera)
        })
      },

      // 添加 控制 
      createControls() {
        this.controls = new OrbitControls(this.camera, this.renderer.domElement);

        // 添加 惯性 效果
        // this.controls.enableDamping = true
        // this.controls.dampingFactor = 0.01

        // 自动旋转
        this.controls.autoRotate = true
        // this.autoRotateSpeed = 0.5

        //监听鼠标、键盘事件
        this.controls.addEventListener('change', () => {

          // 添加 后续 逻辑处理
          // .................

          this.renderer.render(this.scene, this.camera);
        });
      },

      // 旋转 
      animate(model) {
        requestAnimationFrame(this.animate)

        this.controls.update()

        // model.rotation.x += 0.01

        this.renderer.render(this.scene, this.camera)
      }
    }
  }
</script>

<style lang="less" scoped>
  // .s { background-color: #000; }
</style>
